/*
 * @FileName: aiko_bus_transport.c
 * @Author: GreyQiu qiushaogui@aikosolar.com
 * @Date: 2023-08-21 16:44:35
 * @LastEditors: GreyQiu qiushaogui@aikosolar.com
 * @LastEditTime: 2023-09-13 09:07:36
 * @Description: 
 * 
 * Copyright (c) 2023, All Rights Reserved. 
 */

#include "aiko_bus_transport.h"
#include "bsp.h"

uint8_t tx_buffer[WIFI_TX_LEN_MAX];

/* 高位字节的CRC 值 */
static unsigned char auchCRCHi[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1,
    0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
    0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00,
    0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81,
    0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1,
    0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81,
    0x40
} ;

/* 低位字节的CRC 值 */
static char auchCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
    0x05, 0xC5, 0xC4,
    0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB,
    0x0B, 0xC9, 0x09,
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE,
    0xDF, 0x1F, 0xDD,
    0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2,
    0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    0x36, 0xF6, 0xF7,
    0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E,
    0xFE, 0xFA, 0x3A,
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B,
    0x2A, 0xEA, 0xEE,
    0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27,
    0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
    0x63, 0xA3, 0xA2,
    0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD,
    0x6D, 0xAF, 0x6F,
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8,
    0xB9, 0x79, 0xBB,
    0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4,
    0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    0x50, 0x90, 0x91,
    0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94,
    0x54, 0x9C, 0x5C,
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59,
    0x58, 0x98, 0x88,
    0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D,
    0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
    0x41, 0x81, 0x80,
    0x40
};

unsigned short CRC16(unsigned char *puchMsg, unsigned short usDataLen)
{
    unsigned char uchCRCHi = 0xFF ; /* CRC 的高字节初始化 */
    unsigned char uchCRCLo = 0xFF ; /* CRC 的低字节初始化 */
    unsigned uIndex = 0; /* CRC 查询表索引 */
    uint16_t len = usDataLen;
    while (len--) {
        uIndex = uchCRCLo ^ *puchMsg++ ; /* 计算 CRC */
        uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex];
        uchCRCHi = auchCRCLo[uIndex] ;
    }

    return (uchCRCHi << 8 | uchCRCLo) ;
}

/**
 * @description: 回复操作结果
 * @param {uint8_t} cmd
 * @param {uint8_t} err_code
 * @return {*}
 */
void aiko_bus_ack_err(uint8_t cmd, uint8_t err_code)
{
    uint16_t idx = 0;

    tx_buffer[idx++] = 0x7e; // 帧头
    tx_buffer[idx++] = 0; // 帧序号高8位
    tx_buffer[idx++] = 0; // 帧序号低8位
    tx_buffer[idx++] = cmd;  // 命令码
    tx_buffer[idx++] = err_code; // 操作结果
    tx_buffer[idx++] = 0; // 数据长度高8位
    tx_buffer[idx++] = 0; // 数据长度低8位

    uint16_t checksum = CRC16(&tx_buffer[1], idx - 1);
    tx_buffer[idx++] = checksum >> 8; // 校验和高8位
    tx_buffer[idx++] = checksum & 0x00FF; // 校验和低8位

    tx_buffer[idx++] = 0x7e;
    // log_debug("err_code = %d --- crc = 0x%04x\n", err_code, checksum);
    bsp_wifi_uart_send(tx_buffer, idx);
}

/**
 * @description: 回复数据
 * @param {uint8_t} cmd
 * @param {uint8_t} err_code
 * @return {*}
 */
void aiko_bus_ack_data(uint8_t cmd, uint8_t * data, uint16_t len)
{
    uint16_t idx = 0;

    tx_buffer[idx++] = 0x7e; // 帧头
    tx_buffer[idx++] = 0; // 帧序号高8位
    tx_buffer[idx++] = 0; // 帧序号低8位
    tx_buffer[idx++] = cmd;  // 命令码
    tx_buffer[idx++] = ACK_SUCCESS; // 操作码
    tx_buffer[idx++] = len >> 8; // 数据长度高8位
    tx_buffer[idx++] = len & 0xFF; // 数据长度低8位

    for (uint16_t i = 0; i < len; i++)
    {
        tx_buffer[idx++] = data[i]; // 1字节数据
    }
    

    uint16_t checksum = CRC16(&tx_buffer[1], idx - 1);
    tx_buffer[idx++] = checksum >> 8; // 校验和高8位
    tx_buffer[idx++] = checksum & 0x00FF; // 校验和低8位

    tx_buffer[idx++] = 0x7e;
    // log_debug("daiko_bus_ack_data: idx = %d\n", idx);
    bsp_wifi_uart_send(tx_buffer, idx);
}

/**
 * @description: 传输层数据帧的解析处理
 * @param buffer
 * @param len
 * @return
 */


/**
 * @description: 传输层数据帧的解析处理, 自动从入参中的命令表中查找命令回调
 * @param buffer 要解析的帧地址
 * @param len 要解析的帧长度
 * @param cmd_tbl 命令表
 * @param tbl_len 命令表长度
 * @return NONE
 */
void aiko_bus_transport(uint8_t * buffer, uint32_t len, cmd_obj_t * cmd_tbl, uint16_t tbl_len)
{
    log_debug("data get: len = %d\n", len);
    for (size_t i = 0; i < 11; i++)
    {
        log_debug("0x%02x ", buffer[i]);
    }
    log_debug("......\n");

    // 帧长度异常，不响应
    if (len < FRAME_HEAD_LEN + 2) {
        return;
    }
    // 帧头帧尾判断不符合协议规定，不响应
    if (buffer[0] != FRAME_HEADER || buffer[len - 1] != FRAME_TAIL) {
        log_debug("invalid package....\n");
        return;
    }
    uint8_t cmd = buffer[MSG_CMD_IDX]; // 获取命令码
    uint8_t operate = buffer[MSG_OPRATION_IDX]; // 获取操作码
    cmd_obj_t * obj = NULL;
    // log_debug("cmd = %d\n", cmd);
    // log_debug("operate = %d\n", operate);
    // 从表中顺序查找命令，如果命令表长度很大，建议使用二分查找
    uint8_t i;
    for (i = 0; i < tbl_len; i++) {
        if (cmd_tbl[i].cmd == cmd) {
            obj = &cmd_tbl[i];
            break;
        }
    }

    // 
    // if (i >= tbl_len) {
    //     aiko_bus_ack_err(cmd, ERROR_PERMISSION); // 回复无操作权限
    //     return;
    // }
    // 判断命令是否支持
    if (obj == NULL || (obj->permission & operate) == 0) {
        log_debug("cmd error...\n");
        aiko_bus_ack_err(cmd, ACK_ERROR); // 回复无操作权限
        return;
    }
    uint16_t datalen1 = (buffer[MSG_DATA_LEN_H_IDX] << 8) + buffer[MSG_DATA_LEN_L_IDX]; // 获取字段中的数据长度
    uint16_t datalen2 = len - FRAME_HEAD_LEN - 4; // 实际的数据长度 = 帧长度 - 头部长度 - 4(CRC2字节 + 头尾2字节)

    // 判断数据长度是否合法
    if (operate == OPERATE_W) { // 如果是读操作
        if (datalen1 != datalen2) {
            log_debug("data lenth error w...\n");
            aiko_bus_ack_err(cmd, ACK_ERROR); // 回复数据长度错误
            return;
        }
    } else if (operate == OPERATE_R) {
        if (datalen2 != 0) {
            log_debug("data lenth error r...\n");
            aiko_bus_ack_err(cmd, ACK_ERROR); // 回复数据长度错误
            return;
        }
    } else {
        aiko_bus_ack_err(cmd, ACK_ERROR); // 回复无权限
    }


    // 校验数据
    uint16_t chksum1 = (buffer[MSG_DATE_IDX + datalen2] << 8) + buffer[MSG_DATE_IDX + datalen2 + 1]; // 获取校验和
    uint16_t chksum2 = CRC16(&buffer[1], FRAME_HEAD_LEN + datalen2); // 计算校验和
    // log_debug("chksum1 = 0x%04x     chksum2 = 0x%04x\n", chksum1, chksum2);
    if (chksum1 != chksum2) {
        log_debug("crc error...\n");
        aiko_bus_ack_err(cmd, ACK_ERROR); // 回复校验码错误
        return;
    };

    // 执行回调
    if (obj->handler != NULL) {
        obj->handler(cmd, buffer + MSG_DATE_IDX, datalen2, operate);
    }
}
